#!/usr/bin/env zx
const esprima =  require('esprima');
const estraverse =  require('estraverse');
const escodegen =  require('escodegen');
let utilsBuffer 
if(!await isTestTypeYes()){
    console.log(chalk.yellow('🔑 请确保您的项目是 uniapp 或 小程序 ，并在根目录执行的目录'))
}else{
    const q1 = await question('请选择您的操作？add 添加 del 删除？  ',{
        choices: Object.keys(process.env)
    });
    let msgok = '', type = 'start',qName = [],watData={};
    if(q1.includes('add')){
        watData = await setUp();
        msgok = '完成导入'
        type = 'start'
    }else{
        watData = await delUp();
        msgok = '删除完成'
        type = 'delFunc'
    }
    
    if (watData) {
        qName = await logList(watData.pkgName);
        utilsBuffer = await readFile()
        circulation(qName,watData.pkgData,type).then(async(nameList)=>{
            await fs.writeFile(process.cwd()+'/common/utils.js',utilsBuffer)
            nameList.length && console.log(chalk.blue("✔ 包[ "+nameList+" ]"+msgok))
        })
    }
}

/**
 * 删除函数
 * @returns 
 */
async function delUp(){
    let pkgName = [],pkgData={}
    try {
        pkgData = require(process.cwd()+'/common/utils.js')
        pkgName = Object.keys(pkgData);
        if(!pkgName.length){
            console.log( chalk.yellow("⚠  暂无可删除的包"));
           return 
        }
    } catch (error) {
        console.log( chalk.yellow("⚠  不符合预期"));
        return 
    }
    console.log("支持删除的包内容如下：");
    return {
        pkgData,
        pkgName
    }
    // console.log("您选择的是",q1.split(' '));
}

// 添加函数
async function setUp(){
    const pkgData = require('./pkg');
    let pkgName = Object.keys(pkgData);
    try {
        let packaget = Object.keys(require(process.cwd()+'/common/utils.js'));
        pkgName = pkgName.filter(item=>!packaget.includes(item))

        if(!pkgName.length){
            console.log( chalk.yellow("⚠  暂无可导入的包或包均已导入"));
           return 
        }
    } catch (error) {
        
    }
    console.log("支持可导入的包内容如下：");
    return {
        pkgData,
        pkgName
    }
}


/**
 * log 打印
 * @param {array} pkgName 
 * @returns 
 */
 async function logList(pkgName=[]){
    console.log(chalk.blue("ID : FunctionName"));
    pkgName.forEach((item,index)=>{
        console.log( chalk.blue(index+':',item))
    })
    let q1 = await question('(支持空格隔开导入多个函数)\n请选择您要删除的函数名或id？  ',{
        choices: Object.keys(process.env)
    });

    if(q1.includes('--all')){
        return pkgName
    }
    let qName = q1.split(' ').filter(item=>item).map(item=>{
        if(!isNaN(parseInt(item))){
            return pkgName[item]||item
        }
        return item
    })
    console.log("您选择的是",qName);
    return qName
}

/**
 * 轮训执行
 * @param {array} qName 
 * @param {object} pkgData 
 * @param {string} type 
 * @returns 
 */
async function circulation(qName,pkgData,type='start'){
    utilsBuffer = await readFile()
    let func = {
        start:start,
        delFunc:delFunc
    }
    let nameList = []
    return qName.reduce((resolve,item,index)=>{
        return resolve.then(async ()=>{
            if(!pkgData[item]){
                console.log( chalk.red("❌ error：您选择的包["+item+"]不存在！！"));
                return
            }else{
                utilsBuffer = await func[type](utilsBuffer,item,pkgData)
                nameList.push(item)
            }
        })
    },Promise.resolve()).then(res=>nameList)
}

/**
 * 创建目录
 *  */ 
async function mkdir(name){
    try{
        return await $`mkdir ${name}`;
    }catch(err){
        return ''
    }
}
/**
 * 判断文件是否存在
 *  */ 
async function isCommonFile(){
    return (await fs.readdir(process.cwd())).includes('common');
}
/**
 * 判断项目类型
 *  */ 
async function isTestTypeYes(){
    const testProjectType = ['./manifest.json','./project.config.json']
    return await Promise.any(testProjectType.map(item=>fs.readFile(item))).then(res=>true,err=>false)
}
/**
 * 读取文件
 * @param {*} name 
 * @returns 
 */
async function readFile(name='utils.js'){
    return await fs.readFile(process.cwd()+'/common/'+name,"utf8").catch(()=>'');
}
async function createFile(name='utils.js'){
    return await fs.writeFile(process.cwd()+'/common/'+name,'')
}

async function start(utilsBuffer='',q1='',pkgData={}){
    if(!await isCommonFile()){
        await mkdir('common');
        await createFile();
    }
    if(!utilsBuffer){
        // 基础文件
        console.log("➕  创建文件")
        const basicsJS = [
            `;var ${q1} = ${pkgData[q1].toString()};`,
            `module.exports = {`,
            `   ${q1}:${q1}`,
            ` };`
        ].join('\n')
        utilsBuffer = basicsJS
    }else{
        try{
            let utilsJS = require(process.cwd()+'/common/utils.js');
            if(Object.keys(utilsJS).includes(q1)){
                console.log( chalk.yellow(q1+'函数已存在'))
                return 
            }else{
                // 导出方式
                utilsBuffer = await parseModule(utilsBuffer,q1,pkgData)
               
            }
        }catch(err){
            utilsBuffer =  await parseModule(utilsBuffer,q1,pkgData)
        }
    }
    return utilsBuffer
}
/**
 * 删除
 * @param {*} utilsBuffer 
 * @param {*} q1 
 * @param {*} pkgData 
 * @returns 
 */
async function delFunc(utilsBuffer='',q1='',pkgData={}){
    let AST = esprima.parseScript(utilsBuffer)
    let utilsName
    console.log("🖍  开始删除"+q1+"文件...")
    estraverse.traverse(AST,{
        enter:(node,parent)=>{
            if(node.type === 'ExpressionStatement' && node.expression.type === 'AssignmentExpression'&&node.expression.operator==='='){
                let leftAST = node.expression.left
                let rightAST = node.expression.right
                estraverse.traverse(leftAST,{
                    enter:(left)=>{
                        if(left.type==='Identifier' && left.name === 'module'){
                            //  解析右侧的等于至 rightAST
                            if(rightAST.type === 'Identifier'){
                                utilsName = rightAST.name
                            }
                            if(rightAST.type === 'ObjectExpression'){
                                utilsName = '!ObjectExpression'
                                // 删除函数
                                rightAST.properties.splice(rightAST.properties.findIndex(item=>item?.key?.name === q1),1)
                            }
                        }
                    }
                })
                
            }
        }
    })
    if(utilsName&&utilsName !== '!ObjectExpression'){
       let index =  AST.body.findIndex(node=>node.type === 'ExpressionStatement' && node.expression.type === 'AssignmentExpression' &&node.expression.operator==='=' && node.expression.left.object.name === utilsName && node.expression.left.property.name === q1)
       AST.body.splice(index,1)
       return escodegen.generate(AST)
    }
    let kind =['var','let','const']
    let index =  AST.body.findIndex(item=>(item.type === 'FunctionDeclaration' && item.id.name === q1));
    let bodyIndex  = AST.body.findIndex(item=>item.type ==='VariableDeclaration' && kind.includes(item.kind)
                         && item?.declarations?.findIndex(it=>it.type==='VariableDeclarator'&&it?.id?.name===q1)>=0);
    let body =  AST.body[bodyIndex];
    // console.log(AST.body);
    if(index>-1){
        AST.body.splice(index,1)
    }else if(bodyIndex>-1){
        if(body.length>1){
            body.declarations.splice(body.declarations.findIndex(item=>item.id.name===q1),1);
            AST.body[bodyIndex] = body
        }else{
            AST.body.splice(bodyIndex,1)
        }
    }
    return escodegen.generate(AST)
}


/**
 * 解析AST
 * @param {*} utilsBuffer 
 * @param {*} q1 
 * @param {*} pkgData 
 * @returns 
 */
async function parseModule(utilsBuffer='',q1='',pkgData={}){
    
    console.log("🖍  开始解析"+q1+"文件...")
    // console.log(esprima.parseScript(pkgData[q1].toString()+';'))
    let AST = esprima.parseScript(utilsBuffer)
    // pkgParseModule(q1)
    let utilsName
    estraverse.traverse(AST,{
        enter:(node)=>{
            if(node.type === 'ExpressionStatement' && node.expression.type === 'AssignmentExpression'&&node.expression.operator==='='){
                let leftAST = node.expression.left
                let rightAST = node.expression.right
                estraverse.traverse(leftAST,{
                    enter:(left)=>{
                        if(left.type==='Identifier' && left.name === 'module'){
                            //  解析右侧的等于至 rightAST
                            if(rightAST.type === 'Identifier'){
                                utilsName = rightAST.name
                            }
                            if(rightAST.type === 'ObjectExpression'){
                                utilsName = '!ObjectExpression'
                                let ASTS = {
                                    type: 'Property',
                                    key:{ type: 'Identifier', name: q1 },
                                    computed: false,
                                    value:{ type: 'Identifier', name: q1 },
                                    kind: 'init',
                                    method: false,
                                    shorthand: false
                                  } 
                                // 追加函数
                                rightAST.properties.push(ASTS);
                            }
                        }
                    }
                })
                
            }
        }
    })
    let JScode = escodegen.generate(AST)
    if(!utilsName){
        // 这是一个未知情况
        return utilsBuffer
    }else if(utilsName && utilsName !== '!ObjectExpression'){
        let JScope = `${utilsName}.${q1} = ${pkgData[q1].toString()};\n`
        let index = AST.body.findIndex(item=>item.type==='VariableDeclaration' && item?.declarations?.findIndex(it=>it.type==='VariableDeclarator'&&it?.id?.name==utilsName)>=0)
        AST.body.splice(index+1,0,esprima.parseScript(JScope).body[0])
        JScode = escodegen.generate(AST);
        return JScode;
        // 共有对象 utilsName
    }else if(utilsName === '!ObjectExpression'){
        // ObjectExpression
        let JScope = `var ${q1} = ${pkgData[q1].toString()};\n`
        return JScope+JScode;
    }else{
        // console.log(AST)
        if(utilsBuffer.includes('export')){
            console.log( chalk.yellow("? 文件为ESM"));
        }
        return Error("错误"),utilsBuffer
    }
}


/**
 * 解析原 模块语法树
 */
 async function pkgParseModule(q1){
    let pkg = await fs.readFile('./pkg.js',"utf8").catch(console.error);
    let pkgAST = esprima.parseScript(pkg);
    let mut 
    estraverse.traverse(pkgAST,{
        enter:(node)=>{
            if(node.type === 'ExpressionStatement' && node.expression.type === 'AssignmentExpression'&&node.expression.operator==='='){
                let leftAST = node.expression.left
                let rightAST = node.expression.right
                estraverse.traverse(leftAST,{
                    enter:(left)=>{
                        if(left.type==='Identifier' && left.name === 'module'){
                            if(rightAST.type === 'ObjectExpression'){
                                mut = rightAST.properties.filter(item=>item.key.name === q1 && item.key.type==='Identifier')
                                // console.log("mut",mut)
                            }
                        }
                    }
                })
                
            }
        }
    })
    return mut
 }